#pragma once
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
#include <netinet/in.h>
#include "Log.hpp"
#include "Inetaddr.hpp"
#include <functional>
#include <memory>
#include <fcntl.h>

namespace SocketModule
{  
    enum ERSET
    {
        SOCKET_ERROR = 1,
        BIND_ERROR,
        LISTEN_ERROR,
        ACCEPT_ERROR
    };
    inline void Die(int num)
    {
        ::exit(num);
    }
    using namespace LogModule;
    class Socket;
    using SockPtr = std::shared_ptr<Socket>;
    class Socket
    {
    public:
        Socket() {}
        virtual ~Socket() {}
        virtual void CreateSocket() = 0;
        virtual void bind(uint16_t port) = 0;
        virtual void listen() = 0;
        virtual void connect() = 0;
        virtual int Acceptor(InetAddr* out, int*) = 0;
        virtual void SetSockOpt() = 0;
        virtual int FD() = 0;
        virtual int ToRecv(std::string* out) = 0;
        virtual int ToSend(const std::string& in) = 0;
        virtual void BuildTckSocket(uint16_t port)
        {
            CreateSocket();
            SetSockOpt();
            bind(port);
            listen();
        }
    };

    const int gdefaultfd = -1;
#define BACKLOG 8
    class TcpSocket : public Socket
    {
    private:
        int _listensocketfd;
        int _socketfd;
        uint16_t _port;
    public:
        TcpSocket(int listensockfd = gdefaultfd) :_listensocketfd(listensockfd)
        {

        }
        ~TcpSocket() {
            //socketfd让用户去关闭
            if (_listensocketfd != gdefaultfd)
                ::close(_listensocketfd);
        }
        virtual void CreateSocket()
        {
            _listensocketfd = ::socket(AF_INET,SOCK_STREAM,0 );
            if (_listensocketfd < 0)
            {
                LOG(LogLevel::ERROR) << "create socket error";
                Die(ERSET::SOCKET_ERROR);
            }
            LOG(LogLevel::INFO) << "create socket success: " << _listensocketfd;
        }
        virtual void bind(uint16_t port)
        {
            //傻逼了，一开始传入了一个_port!!!s
            InetAddr server(port);
            int n = ::bind(_listensocketfd, (sockaddr *)(server.addr()), server.Len());
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "bind error";
                Die(ERSET::BIND_ERROR);
            }
            LOG(LogLevel::INFO) << "bind success";
        }
        virtual void listen()
        {
            int n = ::listen(_listensocketfd, BACKLOG);
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "listen error";
                Die(ERSET::LISTEN_ERROR);
            }
            LOG(LogLevel::INFO) << "listen success";
        }
        virtual int ToRecv(std::string* out)
        {
            char buffer[1024];
            int n = ::recv(_listensocketfd,buffer,sizeof(buffer)-1,0);
            if (n > 0)
            {
                buffer[n] = 0;
                *out = buffer;
            }
            return n;
        }
        virtual int ToSend(const std::string& in)
        {
            int n = ::send(_listensocketfd,in.c_str(),in.size(),0);
            return n;
        }
        virtual void connect() {}
        virtual int Acceptor(InetAddr* out,int *aerrno) {
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            LOG(LogLevel::INFO) << "wait accept...";
            int socketfd = ::accept(_listensocketfd,(sockaddr*)(&peer),&len);
            if (socketfd < 0){
                LOG(LogLevel::ERROR) << "accept error";
                *aerrno = errno;
                // Die(ERSET::ACCEPT_ERROR);
                return socketfd;
            }
            //把新连接进入的设置为非阻塞 -先不搞
            // SetNonBlock(socketfd);/
            LOG(LogLevel::ERROR) << "accept success: " << socketfd;
            InetAddr client(peer);
            *out = client;
            return socketfd;
        }
        virtual void SetSockOpt(){
            int opt = 1;
            int n = ::setsockopt(_listensocketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
            (void)n;
        }
        int FD() {return _listensocketfd;}
    };
}